package com.patrick.open.gateway.server.config;

import com.alibaba.fastjson.serializer.SerializeConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.patrick.open.api.core.common.OpenTkMap;
import com.patrick.open.api.core.mapper.Db;
import com.patrick.open.gateway.server.exception.GlobalExceptionConfiguration;
import com.patrick.open.api.core.utils.SpringContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.autoconfigure.jackson.JacksonProperties;
import org.springframework.boot.web.reactive.error.ErrorWebExceptionHandler;
import org.springframework.cloud.gateway.route.InMemoryRouteDefinitionRepository;
import org.springframework.cloud.gateway.route.RouteDefinitionLocator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.reactive.result.view.ViewResolver;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author patrick
 * @date 2020/6/8 10:12 下午
 * @Des 网关配置类
 * 最簡單的事是堅持，最難的事還是堅持
 */
@Configuration
@Slf4j

public class OpenConfiguration {
  /**
   * SpringContext
   *
   * @return
   */
  @Bean
  @ConditionalOnMissingBean(SpringContextHolder.class)
  public SpringContextHolder springContextHolder() {
    SpringContextHolder holder = new SpringContextHolder();
    log.info("SpringContextHolder [{}]", holder);
    return holder;
  }

  /**
   * 自定义异常处理[@@]注册Bean时依赖的Bean，会从容器中直接获取，所以直接注入即可
   *
   * @param viewResolversProvider
   * @param serverCodecConfigurer
   * @return
   */
  @Bean
  @Primary
  @Order(Ordered.HIGHEST_PRECEDENCE)
  public ErrorWebExceptionHandler errorWebExceptionHandler(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) {

    GlobalExceptionConfiguration globalExceptionConfiguration = new GlobalExceptionConfiguration();
    globalExceptionConfiguration.setViewResolvers(viewResolversProvider.getIfAvailable(Collections::emptyList));
    globalExceptionConfiguration.setMessageWriters(serverCodecConfigurer.getWriters());
    globalExceptionConfiguration.setMessageReaders(serverCodecConfigurer.getReaders());
    log.info("ErrorWebExceptionHandler [{}]", globalExceptionConfiguration);
    return globalExceptionConfiguration;
  }

  @Bean
  @Primary
  public JacksonProperties jacksonProperties(JacksonProperties properties) {
    properties.setDefaultPropertyInclusion(JsonInclude.Include.NON_NULL);
    properties.getSerialization().put(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, true);
    properties.setDateFormat("yyyy-MM-dd HH:mm:ss");
    properties.setTimeZone(TimeZone.getTimeZone("GMT+8"));
    return properties;
  }

  /**
   * 全局同意fastJson，支持国产
   *
   * @param converters
   * @return
   */
  @Bean
  public HttpMessageConverters httpMessageConverters(List<HttpMessageConverter<?>> converters) {
    //构建fastConvert对象
    FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
    //自定义fastJson配置
    FastJsonConfig fastJsonConfig = new FastJsonConfig();
    SerializerFeature[] serializerFeatures = new SerializerFeature[]{
      //null =>> 0
      SerializerFeature.WriteNullNumberAsZero,
      //null =>>[]
      SerializerFeature.WriteNullListAsEmpty,
      //null =>>""
      SerializerFeature.WriteNullStringAsEmpty,
      //null =>>false
      SerializerFeature.WriteNullBooleanAsFalse,
      SerializerFeature.WriteDateUseDateFormat,
      SerializerFeature.DisableCircularReferenceDetect,
    };
    // 处理Long转String js精度丢失问题
    SerializeConfig serializeConfig = SerializeConfig.globalInstance;
    serializeConfig.put(BigInteger.class, ToStringSerializer.instance);
    serializeConfig.put(Long.class, ToStringSerializer.instance);
    serializeConfig.put(Long.TYPE, ToStringSerializer.instance);

    fastJsonConfig.setSerializeConfig(serializeConfig);
    fastJsonConfig.setSerializerFeatures(serializerFeatures);
    fastJsonConfig.setCharset(StandardCharsets.UTF_8);
    fastConverter.setFastJsonConfig(fastJsonConfig);
    HttpMessageConverter<?> converter = fastConverter;

    return new HttpMessageConverters(converter);
  }

  @Bean
  @Primary
  public SwaggerProvider swaggerProvider(RouteDefinitionLocator routeDefinitionLocator) {
    return new SwaggerProvider(routeDefinitionLocator);
  }

  /**
   * 动态路由加载
   *
   * @return
   */
  @Bean
  public JdbcRouteDefinitionLocator jdbcRouteDefinitionLocator(InMemoryRouteDefinitionRepository repository) {
    JdbcRouteDefinitionLocator jdbcRouteDefinitionLocator = new JdbcRouteDefinitionLocator(jdbcTemplate, repository);
    log.info("JdbcRouteDefinitionLocator [{}]", jdbcRouteDefinitionLocator);
    return jdbcRouteDefinitionLocator;
  }
}
